#include "IAudioOut.h"



IAudioOut::IAudioOut(IBuffer2 *buf)
{
	this->_deviceList = NULL;
	this->_deviceTable = NULL;
	this->_currentDevice = NULL;
	this->_run = false;
	this->_buf = buf;
	this->_sourceRun = false;
	this->_useBuf = 0;

	buf->SubscribeEnoughData(&IAudioOut::treat, 4410, this);
}

IAudioOut::~IAudioOut()
{
	if (this->_run = true)
		this->_run = false;
	delete this->_deviceList;
	delete this->_deviceTable;

	if (this->_currentDevice != NULL)
		alcCloseDevice(this->_currentDevice);
}

char **IAudioOut::detectDevice()
{
	// test if lister is avaible 
	if(alcIsExtensionPresent(NULL, "ALC_ENUMERATE_ALL_EXT"))
	{
		int numDevice = 0;
		char *ptr;

		// delete old device list if exist
		if (this->_deviceList != NULL)
		{
			delete this->_deviceList;
		}
		// get list of device
		this->_deviceList = (char*)alcGetString
			(
			NULL, 
			ALC_ALL_DEVICES_SPECIFIER
			);
		if (this->_deviceList == "")
		{
			return (char **)-1;
		}
		// count devices
		ptr = this->_deviceList;
		while( ptr && *ptr )
		{
			while (*ptr != NULL)
				++ptr;
			ptr++;
			numDevice++;
		}
		// delete old device table if exist
		if (this->_deviceTable != NULL)
		{
			delete this->_deviceTable;
		}
		// create new device table
		this->_deviceTable = new char *[numDevice];
		// complete deviceTable
		ptr = this->_deviceList;
		numDevice = 0;
		while( ptr && *ptr )
		{
			this->_deviceTable[numDevice] = ptr;
			while (*ptr != NULL)
				++ptr;
			ptr++;
			numDevice++;
		}
		this->_deviceTable[numDevice] = NULL;
	}
	else
	{
		return NULL;
	}
	return this->_deviceTable;
}

void IAudioOut::selectOutDevice(char *deviceName)
{
	if (this->_currentDevice != NULL)
		alcCloseDevice(this->_currentDevice);
	this->_currentDevice = alcOpenDevice(deviceName);
}

void IAudioOut::selectOutDevice(int deviceId)
{
	if (this->_currentDevice != NULL)
		alcCloseDevice(this->_currentDevice);
	this->_currentDevice = alcOpenDevice(this->_deviceTable[deviceId]);
}

void IAudioOut::autoSelectOutDevice()
{
	if (this->_currentDevice != NULL)
		alcCloseDevice(this->_currentDevice);
	this->_currentDevice = alcOpenDevice(NULL);
}

void IAudioOut::start()
{
	if (this->_currentDevice == NULL)
		this->autoSelectOutDevice();
	this->_ctx = alcCreateContext(this->_currentDevice, NULL);
	alcMakeContextCurrent(this->_ctx);
	this->checkError("alcMakeContextCurrent");
	alGenSources(1, &this->_source);
	this->checkError("alGenSources");

	this->_run = true;
	this->_useBuf = 0;
}

void IAudioOut::stop()
{
	this->_run = false;
	this->_sourceRun = false;
}

void IAudioOut::run()
{
	int processed;
	
	if (this->_run && this->_source)
	{
		this->checkRun();
			alGetSourcei(this->_source, AL_BUFFERS_PROCESSED, &processed);
			while (processed-- > 0)
			{
				ALuint buffer;
				alSourceUnqueueBuffers(this->_source, 1, &buffer);
				this->checkError("alSourceUnqueueBuffers");
				_freeALBuf.push_back(buffer);
				this->_usedALBuf.remove(buffer);

			}
			if (!_freeALBuf.size())
			{
				ALuint buffer;
				alGenBuffers(1, &buffer);
				this->checkError("alGenBuffers");
				this->stream(buffer);
				alSourceQueueBuffers(this->_source, 1, &buffer);
				this->checkError("alBufferDataNEW");
				this->_usedALBuf.push_back(buffer);
				std::cout << "new buffer " << buffer << std::endl;
				std::cout << this->_usedALBuf.size() << std::endl;
			}
			else
			{
				//std::cout << "reuse" << std::endl;
				this->stream(_freeALBuf.back());
				this->checkError("alBufferDataReuse");
				alSourceQueueBuffers(this->_source, 1, &_freeALBuf.back());
				this->_usedALBuf.push_back(_freeALBuf.back());
				_freeALBuf.pop_back();
			}
			if (!this->_sourceRun)
			{
				alSourcePlay(this->_source);
				//std::cout << "play" << std::endl;
			}
	}
}

void IAudioOut::checkError(char* str)
{
	int error = alGetError();

	//if(error != AL_NO_ERROR)
	//	std::cout << "Error " << error <<  " in : " << str << std::endl;;
}

void IAudioOut::stream(ALuint buf)
{
	//std::cout << (int) data << std::endl;

	alBufferData(buf, AL_FORMAT_MONO16, this->_buf->Read(4410), 4410, 44100);
}

void IAudioOut::checkRun()
{
	ALenum state;

	alGetSourcei(this->_source, AL_SOURCE_STATE, &state);
	this->_sourceRun = (state == AL_PLAYING);
}

void IAudioOut::treat(IBuffer2*buffer, void*data)
{
	((IAudioOut*)data)->run();
	
}